#pragma once

#include <vector>
// #include <cstddef> // size_t
#include <string> // ssize_t
#include <algorithm>
// #include <stddef.h>

// muduo参考Netty设计：预留区+读区+写区
// 注意：上层视角/对上层而言：
// | kInitPrependSize预留区 | 读走的/空闲读区 readableIndex有数据可读区 | 已写的 writeIndex空闲可写区 |
// TODO: 实际上，预留区暂未使用；上层读写分别用两个Buffer读或写，而不是一个同时读写，可以优化设计
class Buffer
{
public:
    // 用户可以访问修改
    // TODO: 设计更合理的配置初始化大小方式
    static const size_t kInitPrependSize = 8;      // 初始预留区大小，单位B
    static const size_t kInitReadWriteSize = 1024; // 初始读写区大小

    explicit Buffer(size_t initReadWriteSize = kInitReadWriteSize);

    // 内核->Buffer->库本身/服务端，readv()时往空闲可写区写
    ssize_t readFd(int fd, int *saveErrno);
    // 库本身/服务端->buffer->内核，write()时从有数据可读区读
    ssize_t writeFd(int fd, int *saveErrno);
    // void ensureWritableBytes(size_t len);

    // 扩容，[data, data+needWriteSize]追加到写区
    void appendWrite(const char *data, size_t needWriteSize);

    // 注意：上层视角/对上层而言：空闲预留区+空闲读区、有数据可读区、空闲可写区大小
    // TODO: 有些方法不一定public、不一定用到，可以调整
    size_t getPrependableSize() const;
    size_t getReadableSize() const;
    size_t getWritableSize() const;

    char *writableBegin();
    const char *writableBegin() const;
    char *readableBegin();
    const char *readableBegin() const;

    // 取有数据可读区数据
    std::string getAllReadableData();                 // 读所有
    std::string getReadableData(size_t needReadSize); // 读部分
    // 取有数据可读区数据后，索引复位
    void readableIndexReset(size_t needReadSize);
    // 注意：实际上，上层读写分别用两个Buffer读或写，而不是一个同时读写，可以同时复位
    void indexReset();

    // Http用到
    const char *findCrlfPos() const
    {
        const char *crlf = "\r\n";
        const char *crlfPos = std::search(this->readableBegin(), this->writableBegin(), crlf, crlf + 2); // 在可读区找
        return crlfPos == this->writableBegin() ? nullptr : crlfPos;
    }

    void moveReadableIndexTo(const char *end)
    {
        // 上层已读取，处理索引，并判断是否复位
        this->readableIndexReset(end - this->readableBegin());
    }

    void append(const std::string &str)
    {
        this->appendWrite(str.data(), str.size());
    }

    // 用于调试查看可读区内容，相比getAllReadableData()没有置位索引
    std::string getAllReadableDataDebug()
    {
        std::string data(this->readableBegin(), this->getReadableSize());
        return data;
    }

private:
    // socket编程需要裸指针
    char *begin();
    const char *begin() const;

    void expandCapacity(size_t needWriteSize);

    std::vector<char> buffer_;
    // 注意：上层视角/对上层而言：从索引处开始读、写
    size_t readableIndex_;
    size_t writableIndex_;
};
